import type { SubpathCallback, SubpathInputOption, WasmSubpathInstance } from "@/types";
import { capOptions, joinOptions, tSliderOptions, subpathTValueVariantOptions, intersectionErrorOptions, minimumSeparationOptions, separationDiskDiameter, SUBPATH_T_VALUE_VARIANTS } from "@/types";

const subpathFeatures = {
	constructor: {
		name: "Constructor",
		callback: (subpath: WasmSubpathInstance): string => subpath.to_svg(),
	},
	insert: {
		name: "Insert",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined): string => subpath.insert(options.t, SUBPATH_T_VALUE_VARIANTS[options.TVariant]),
		inputOptions: [subpathTValueVariantOptions, tSliderOptions],
	},
	length: {
		name: "Length",
		callback: (subpath: WasmSubpathInstance): string => subpath.length(),
	},
	"length-centroid": {
		name: "Length Centroid",
		callback: (subpath: WasmSubpathInstance): string => subpath.length_centroid(),
	},
	area: {
		name: "Area",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined): string => subpath.area(options.error, options.minimum_separation),
		inputOptions: [intersectionErrorOptions, minimumSeparationOptions],
	},
	"area-centroid": {
		name: "Area Centroid",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined): string => subpath.area_centroid(options.error, options.minimum_separation),
		inputOptions: [intersectionErrorOptions, minimumSeparationOptions],
	},
	"poisson-disk-points": {
		name: "Poisson-Disk Points",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined): string => subpath.poisson_disk_points(options.separation_disk_diameter),
		inputOptions: [separationDiskDiameter],
	},
	evaluate: {
		name: "Evaluate",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined): string => subpath.evaluate(options.t, SUBPATH_T_VALUE_VARIANTS[options.TVariant]),
		inputOptions: [subpathTValueVariantOptions, tSliderOptions],
	},
	"lookup-table": {
		name: "Lookup Table",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined): string => subpath.compute_lookup_table(options.steps, SUBPATH_T_VALUE_VARIANTS[options.TVariant]),
		inputOptions: [
			subpathTValueVariantOptions,
			{
				variable: "steps",
				inputType: "slider",
				min: 2,
				max: 30,
				step: 1,
				default: 5,
			},
		],
	},
	project: {
		name: "Project",
		callback: (subpath: WasmSubpathInstance, _: Record<string, number>, mouseLocation?: [number, number]): string =>
			mouseLocation ? subpath.project(mouseLocation[0], mouseLocation[1]) : subpath.to_svg(),
		triggerOnMouseMove: true,
	},
	tangent: {
		name: "Tangent",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined): string => subpath.tangent(options.t, SUBPATH_T_VALUE_VARIANTS[options.TVariant]),
		inputOptions: [subpathTValueVariantOptions, tSliderOptions],
	},
	normal: {
		name: "Normal",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined): string => subpath.normal(options.t, SUBPATH_T_VALUE_VARIANTS[options.TVariant]),
		inputOptions: [subpathTValueVariantOptions, tSliderOptions],
	},
	"local-extrema": {
		name: "Local Extrema",
		callback: (subpath: WasmSubpathInstance): string => subpath.local_extrema(),
	},
	"bounding-box": {
		name: "Bounding Box",
		callback: (subpath: WasmSubpathInstance): string => subpath.bounding_box(),
	},
	inflections: {
		name: "Inflections",
		callback: (subpath: WasmSubpathInstance): string => subpath.inflections(),
	},
	"intersect-linear": {
		name: "Intersect (Linear Segment)",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string =>
			subpath.intersect_line_segment(
				[
					[80, 30],
					[210, 150],
				],
				options.error,
				options.minimum_separation,
			),
		inputOptions: [intersectionErrorOptions, minimumSeparationOptions],
	},
	"intersect-quadratic": {
		name: "Intersect (Quadratic Segment)",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string =>
			subpath.intersect_quadratic_segment(
				[
					[25, 50],
					[205, 10],
					[135, 180],
				],
				options.error,
				options.minimum_separation,
			),
		inputOptions: [intersectionErrorOptions, minimumSeparationOptions],
	},
	"intersect-cubic": {
		name: "Intersect (Cubic Segment)",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string =>
			subpath.intersect_cubic_segment(
				[
					[65, 20],
					[125, 40],
					[65, 120],
					[200, 140],
				],
				options.error,
				options.minimum_separation,
			),
		inputOptions: [intersectionErrorOptions, minimumSeparationOptions],
	},
	"intersect-self": {
		name: "Intersect (Self)",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string => subpath.self_intersections(options.error, options.minimum_separation),
		inputOptions: [intersectionErrorOptions, minimumSeparationOptions],
	},
	"intersect-rectangle": {
		name: "Intersect (Rectangle)",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string =>
			subpath.intersect_rectangle(
				[
					[75, 50],
					[175, 150],
				],
				options.error,
				options.minimum_separation,
			),
		inputOptions: [intersectionErrorOptions, minimumSeparationOptions],
	},
	"inside-other": {
		name: "Inside (Other Subpath)",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string =>
			subpath.inside_subpath(
				[
					[40, 40],
					[160, 40],
					[160, 80],
					[200, 100],
					[160, 120],
					[160, 160],
					[40, 160],
					[40, 120],
					[80, 100],
					[40, 80],
				],
				options.error,
				options.minimum_separation,
			),
		inputOptions: [intersectionErrorOptions, minimumSeparationOptions],
	},
	curvature: {
		name: "Curvature",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined): string => subpath.curvature(options.t, SUBPATH_T_VALUE_VARIANTS[options.TVariant]),
		inputOptions: [subpathTValueVariantOptions, { ...tSliderOptions, default: 0.2 }],
	},
	split: {
		name: "Split",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined): string => subpath.split(options.t, SUBPATH_T_VALUE_VARIANTS[options.TVariant]),
		inputOptions: [subpathTValueVariantOptions, tSliderOptions],
	},
	trim: {
		name: "Trim",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined): string => subpath.trim(options.t1, options.t2, SUBPATH_T_VALUE_VARIANTS[options.TVariant]),
		inputOptions: [subpathTValueVariantOptions, { ...tSliderOptions, default: 0.2, variable: "t1" }, { ...tSliderOptions, variable: "t2" }],
	},
	offset: {
		name: "Offset",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string => subpath.offset(options.distance, options.join, options.miter_limit),
		inputOptions: [
			{
				variable: "distance",
				inputType: "slider",
				min: -25,
				max: 25,
				step: 1,
				default: 10,
			},
			joinOptions,
			{
				variable: "join: Miter - limit",
				inputType: "slider",
				min: 1,
				max: 10,
				step: 0.25,
				default: 4,
			},
		],
	},
	outline: {
		name: "Outline",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string => subpath.outline(options.distance, options.join, options.cap, options.miter_limit),
		inputOptions: [
			{
				variable: "distance",
				inputType: "slider",
				min: 0,
				max: 25,
				step: 1,
				default: 10,
			},
			joinOptions,
			{
				variable: "join: Miter - limit",
				inputType: "slider",
				min: 1,
				max: 10,
				step: 0.25,
				default: 4,
			},
			{ ...capOptions, isDisabledForClosed: true },
		],
	},
	rotate: {
		name: "Rotate",
		callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string => subpath.rotate(options.angle * Math.PI, 125, 100),
		inputOptions: [
			{
				variable: "angle",
				inputType: "slider",
				min: 0,
				max: 2,
				step: 1 / 50,
				default: 0.12,
				unit: "π",
			},
		],
	},
};

export type SubpathFeatureKey = keyof typeof subpathFeatures;
export type SubpathFeatureOptions = {
	name: string;
	callback: SubpathCallback;
	inputOptions?: SubpathInputOption[];
	triggerOnMouseMove?: boolean;
};
export default subpathFeatures as Record<SubpathFeatureKey, SubpathFeatureOptions>;
